package com.tutucms.common.util;

import com.tutucms.common.data.KeyValue;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Title:HttpClientUtil.java
 *
 * @author wangxudong
 */
public class HttpClientUtil {

    private static final Logger log = LoggerFactory.getLogger(HttpClientUtil.class);

    private static final int MAX_TIMEOUT = 5000;
    private static final int MAX_TOTAL_CONN = 200;
    private static final int MAX_PER_ROUTE_CONN = 20;
    private static RequestConfig requestConfig;
    private static PoolingHttpClientConnectionManager connMgr;

    static {
        try {
            LayeredConnectionSocketFactory lcsf = new SSLConnectionSocketFactory(SSLContext.getDefault());
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("https", lcsf)
                    .register("http", new PlainConnectionSocketFactory())
                    .build();
            connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

            requestConfig = RequestConfig.custom()
                    .setConnectionRequestTimeout(MAX_TIMEOUT)
                    .setConnectTimeout(MAX_TIMEOUT)
                    .setSocketTimeout(MAX_TIMEOUT)
                    .build();
            connMgr.setMaxTotal(MAX_TOTAL_CONN);
            connMgr.setDefaultMaxPerRoute(MAX_PER_ROUTE_CONN);
            SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(MAX_TIMEOUT).build();
            connMgr.setDefaultSocketConfig(socketConfig);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    public static String doGet(String url) {
        return doGet(url, new HashMap<>(), "");
    }

    public static String doGet(String url, Map<String, String> params, String encode) {
        if (encode == null || encode.isEmpty()) {
            encode = "UTF-8";
        }
//        List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size());
//        for (Map.Entry<String, String> entry : params.entrySet()) {
//            NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue());
//            pairList.add(pair);
//        }
//        String paramStr = "";
//        try {
//            paramStr = EntityUtils.toString(new UrlEncodedFormEntity(pairList, encode));
//        } catch (IOException e) {
//            log.error(e.getMessage(), e);
//        }
//        HttpGet httpGet = new HttpGet(params.size() > 0 ? url + "?" + paramStr : url);
//        log.debug("request url: " + (params.size() > 0 ? url + "?" + paramStr : url));
        CloseableHttpResponse response = null;
        try {
            URIBuilder uriBuilder = new URIBuilder(url);
            if (params != null && params.isEmpty()) {
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    uriBuilder.addParameter(entry.getKey(), entry.getValue());
                }
            }
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 " +
                    "(KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");
            httpGet.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9," +
                    "image/webp,*/*;q=0.8");
            httpGet.setHeader("Accept-Charset", "utf-8,GB2312:q=0.7,*;q=0.7");
            httpGet.setHeader("Accept-Encoding", "gzip, deflate");
            httpGet.setHeader("Accept-Language", "zh-CN,zh;q=0.8");
            CloseableHttpClient httpClient = getHttpClient();
            response = httpClient.execute(httpGet);
            int status = response.getStatusLine().getStatusCode();
            log.debug("httpClient response status code: " + status);
            return EntityUtils.toString(response.getEntity(), encode);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (response != null) {
                    EntityUtils.consume(response.getEntity());
                }
            } catch (IOException e) {
                log.error("", e);
            }
        }
        return null;
    }

    public static byte[] doGetByteArray(String url) {
        CloseableHttpResponse response = null;
        ByteArrayOutputStream output = null;
        try {
            URIBuilder uriBuilder = new URIBuilder(url);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 " +
                    "(KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");
            httpGet.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9," +
                    "image/webp,*/*;q=0.8");
            httpGet.setHeader("Accept-Charset", "utf-8,GB2312:q=0.7,*;q=0.7");
            httpGet.setHeader("Accept-Encoding", "gzip, deflate");
            httpGet.setHeader("Accept-Language", "zh-CN,zh;q=0.8");
            CloseableHttpClient httpClient = getHttpClient();
            response = httpClient.execute(httpGet);
            int status = response.getStatusLine().getStatusCode();
            log.debug("httpClient response status code: " + status);

            output = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024 * 4];
            int n;
            while (-1 != (n = response.getEntity().getContent().read(buffer))) {
                output.write(buffer, 0, n);
            }

            return output.toByteArray();
        } catch (Exception e) {

            log.error(e.getMessage(), e);
        } finally {
            try {
                if (response != null) {
                    EntityUtils.consume(response.getEntity());
                }

                if (output != null) {
                    output.close();
                }
            } catch (IOException e) {
                log.error("", e);
            }
        }

        return new byte[0];
    }

    public static long fetchContentLength(String urlStr) throws IOException {
        URL url = new URL(urlStr);
        URLConnection conn = url.openConnection();
        log.debug("get contentLength");
        conn.setReadTimeout(3000);
        conn.setConnectTimeout(3000);
        long size = conn.getContentLength();
        conn.getInputStream().close();
        return size;
    }

    public static String fetchResponseHeader(String urlStr, String name) throws IOException {
        URL url = new URL(urlStr);
        URLConnection conn = url.openConnection();
        log.debug("get header");
        conn.setReadTimeout(3000);
        conn.setConnectTimeout(3000);
        String head = conn.getHeaderField(name);
        conn.getInputStream().close();
        return head;
    }


    /**
     * 发送http请求返回InputStream
     *
     * @param url 带http或者https的url
     * @return
     */
    public static boolean saveGetDate(String url, String savePath) {
        HttpGet httpGet = new HttpGet(url);
        log.debug("request url: " + url);
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 " +
                "(KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");
        httpGet.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9," +
                "image/webp,*/*;q=0.8");
        httpGet.setHeader("Accept-Charset", "utf-8,GB2312:q=0.7,*;q=0.7");
        httpGet.setHeader("Accept-Encoding", "gzip, deflate");
        httpGet.setHeader("Accept-Language", "zh-CN,zh;q=0.8");

        BufferedInputStream bis = null;
        CloseableHttpResponse response = null;
        CloseableHttpClient httpClient = getHttpClient();
        BufferedOutputStream bos = null;
        try {
            File file = new File(savePath);
            MyFileUtil.createNewFileOrDir(file);
            response = httpClient.execute(httpGet);
            int status = response.getStatusLine().getStatusCode();
            log.debug("httpClient response status code:" + status);
            bos = new BufferedOutputStream(new FileOutputStream((file)));
            bis = new BufferedInputStream(response.getEntity().getContent());
            byte[] buffer = new byte[512];  // 512字节缓冲
            int i;
            while ((i = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, i);
            }
            bos.flush();
            return true;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
                if (bis != null) {
                    bis.close();
                }
                if (response != null) {
                    response.close();
                }
            } catch (Exception e) {
                log.error("", e);
            }
        }
        return false;
    }

    /**
     * 发送 POST 请求（HTTP），不带输入数据
     *
     * @param apiUrl
     * @return
     */
    public static KeyValue<Integer, String> doPost(String apiUrl) {
        return doPost(apiUrl, new HashMap<>());
    }

    /**
     * 发送 POST 请求（HTTP），K-V形式
     *
     * @param apiUrl API接口URL
     * @param params 参数map
     * @return key responseCode Content
     */
    public static KeyValue<Integer, String> doPost(String apiUrl, Map<String, Object> params) {
        String httpStr;
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;
        httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 " +
                "(KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");
        httpPost.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9," +
                "image/webp,*/*;q=0.8");
        httpPost.setHeader("Accept-Charset", "utf-8,GB2312:q=0.7,*;q=0.7");
        httpPost.setHeader("Accept-Encoding", "gzip, deflate");
        httpPost.setHeader("Accept-Language", "zh-CN,zh;q=0.8");

        try {
            List<NameValuePair> pairList = new ArrayList<>(params.size());
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry
                        .getValue().toString());
                pairList.add(pair);
            }
            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8")));
            CloseableHttpClient httpClient = getHttpClient();
            response = httpClient.execute(httpPost);
            int status = response.getStatusLine().getStatusCode();
            log.debug("httpClient response status code:" + status);
            if (status == 302) {
                Header[] headers = response.getAllHeaders();
                for (Header header : headers) {
                    if (header.getName().equalsIgnoreCase("Location")) {
                        return new KeyValue<Integer, String>(302, header.getValue());
                    }
                }
            }
            HttpEntity entity = response.getEntity();
            httpStr = EntityUtils.toString(entity, "UTF-8");
            return new KeyValue<>(status, httpStr);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (response != null) {
                    EntityUtils.consume(response.getEntity());
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        return null;
    }


    /**
     * 发送 POST 请求（HTTP），JSON形式
     *
     * @param json json对象
     */
    public static KeyValue<Integer, String> doPost(String apiUrl, Object json) {
        CloseableHttpClient httpClient = getHttpClient();
        String httpStr;
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;
        httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 " +
                "(KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");
        httpPost.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9," +
                "image/webp,*/*;q=0.8");
        httpPost.setHeader("Accept-Charset", "utf-8,GB2312:q=0.7,*;q=0.7");
        httpPost.setHeader("Accept-Encoding", "gzip, deflate");
        httpPost.setHeader("Accept-Language", "zh-CN,zh;q=0.8");

        try {
            StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");//解决中文乱码问题
            stringEntity.setContentEncoding("UTF-8");
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);
            response = httpClient.execute(httpPost);
            int status = response.getStatusLine().getStatusCode();
            log.debug("httpClient response status code:" + status);
            if (status == 302) {
                Header[] headers = response.getAllHeaders();
                for (Header header : headers) {
                    if (header.getName().equalsIgnoreCase("Location")) {
                        return new KeyValue<Integer, String>(302, header.getValue());
                    }
                }
            }
            HttpEntity entity = response.getEntity();
            httpStr = EntityUtils.toString(entity, "UTF-8");
            return new KeyValue<Integer, String>(status, httpStr);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (response != null) {
                    EntityUtils.consume(response.getEntity());
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }

        return null;
    }

    public static CloseableHttpClient getHttpClient() {
        return HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connMgr)
                .build();
    }

    /**
     * 发送 SSL POST 请求（HTTPS），K-V形式
     *
     * @param apiUrl API接口URL
     * @param params 参数map
     * @return
     */
    public static String doPostSSL(String apiUrl, Map<String, Object> params, Map<String, String> headers) {
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory())
                .setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;
        String httpStr = null;

        try {
            List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size());
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue().toString());
                pairList.add(pair);
            }
            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("utf-8")));
            if (headers != null) {
                for (String key : headers.keySet()) {
                    httpPost.setHeader(key, headers.get(key));
                }
            }
            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK) {
                return null;
            }
            HttpEntity entity = response.getEntity();
            if (entity == null) {
                return null;
            }
            httpStr = EntityUtils.toString(entity, "utf-8");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
        return httpStr;
    }

    /**
     * 发送 SSL POST 请求（HTTPS），JSON形式
     *
     * @param apiUrl API接口URL
     * @param json   JSON对象
     * @return
     */
    public static String doPostSSL(String apiUrl, Object json, Map<String, String> headers) {
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory())
                .setConnectionManager(connMgr)
                .setDefaultRequestConfig(requestConfig).build();
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;
        String httpStr = null;

        try {
            StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");// 解决中文乱码问题
            stringEntity.setContentEncoding("UTF-8");
            httpPost.setEntity(stringEntity);
            if (headers != null) {
                for (String key : headers.keySet()) {
                    httpPost.setHeader(key, headers.get(key));
                }
            }
            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            log.info("status Code:" + statusCode);
            if (statusCode != HttpStatus.SC_OK) {
                return null;
            }
            HttpEntity entity = response.getEntity();
            if (entity == null) {
                return null;
            }
            httpStr = EntityUtils.toString(entity, "utf-8");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
        return httpStr;
    }

    /**
     * 创建SSL安全连接
     *
     * @return
     */
    private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
        SSLConnectionSocketFactory sslsf = null;
        try {
            SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial(null, (chain, authType) -> true).build();
            sslsf = new SSLConnectionSocketFactory(sslContext);
        } catch (GeneralSecurityException e) {
            log.error(e.getMessage(), e);
        }
        return sslsf;
    }
//    /**
//     * 发送 SSL POST 请求（HTTPS），K-V形式
//     *
//     * @param apiUrl API接口URL
//     * @param params 参数map
//     * @return
//     */
//    public static String doPostSSL(String apiUrl, Map<String, Object> params) {
//        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
//        HttpPost httpPost = new HttpPost(apiUrl);
//        CloseableHttpResponse response = null;
//        String httpStr = null;
//
//        try {
//            httpPost.setConfig(requestConfig);
//            List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size());
//            for (Map.Entry<String, Object> entry : params.entrySet()) {
//                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry
//                        .getValue().toString());
//                pairList.add(pair);
//            }
//            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("utf-8")));
//            response = httpClient.execute(httpPost);
//            int statusCode = response.getStatusLine().getStatusCode();
//            if (statusCode != HttpStatus.SC_OK) {
//                return null;
//            }
//            HttpEntity entity = response.getEntity();
//            if (entity == null) {
//                return null;
//            }
//            httpStr = EntityUtils.toString(entity, "utf-8");
//        } catch (Exception e) {
//            log.error(e.getMessage(), e);
//        } finally {
//            if (response != null) {
//                try {
//                    EntityUtils.consume(response.getEntity());
//                } catch (IOException e) {
//                    log.error(e.getMessage(), e);
//                }
//            }
//        }
//        return httpStr;
//    }
//
//    /**
//     * 发送 SSL POST 请求（HTTPS），JSON形式
//     *
//     * @param apiUrl API接口URL
//     * @param json   JSON对象
//     * @return
//     */
//    public static String doPostSSL(String apiUrl, Object json) {
//        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
//        HttpPost httpPost = new HttpPost(apiUrl);
//        CloseableHttpResponse response = null;
//        String httpStr = null;
//
//        try {
//            httpPost.setConfig(requestConfig);
//            StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");//解决中文乱码问题
//            stringEntity.setContentEncoding("UTF-8");
//            stringEntity.setContentType("application/json");
//            httpPost.setEntity(stringEntity);
//            response = httpClient.execute(httpPost);
//            int statusCode = response.getStatusLine().getStatusCode();
//            if (statusCode != HttpStatus.SC_OK) {
//                return null;
//            }
//            HttpEntity entity = response.getEntity();
//            if (entity == null) {
//                return null;
//            }
//            httpStr = EntityUtils.toString(entity, "utf-8");
//        } catch (Exception e) {
//            log.error(e.getMessage(), e);
//        } finally {
//            if (response != null) {
//                try {
//                    EntityUtils.consume(response.getEntity());
//                } catch (IOException e) {
//                    log.error(e.getMessage(), e);
//                }
//            }
//        }
//        return httpStr;
//    }
//
//    /**
//     * 创建SSL安全连接
//     *
//     * @return
//     */
//    private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
//        SSLConnectionSocketFactory sslsf = null;
//        try {
//            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
//
//                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//                    return true;
//                }
//            }).build();
//            sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
//
//                @Override
//                public boolean verify(String arg0, SSLSession arg1) {
//                    return true;
//                }
//
//                @Override
//                public void verify(String host, SSLSocket ssl) throws IOException {
//                }
//
//                @Override
//                public void verify(String host, X509Certificate cert) throws SSLException {
//                }
//
//                @Override
//                public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
//                }
//            });
//        } catch (GeneralSecurityException e) {
//            log.error(e.getMessage(), e);
//        }
//        return sslsf;
//    }
}
